package cn.jilian.common.util;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

public class MapUtil {
	
	public static final ObjectMapper objectMapper=new ObjectMapper();
	
	public static void main(String[] args) {
		Map<String,Object> data=new HashMap<>();
		data.put("k1", 1);
		Map<String,Object> k2=new HashMap<>();
		Map<String,Object> k3=new HashMap<>();
		k3.put("k4", "k4");
		k3.put("k5", "k5");
		k2.put("k3", k3);
		k2.put("k6", "k6");
		data.put("k2",k2);
		
		try {
			ObjectWriter writer=objectMapper.writerWithDefaultPrettyPrinter();
			System.out.println(writer.writeValueAsString(data));
			Map<String,Object> rs=flat(data);
			System.out.println(writer.writeValueAsString(rs));
			data=expand(rs);
			System.out.println(writer.writeValueAsString(data));
		}catch(Exception e) {
			
		}
	}
	
	/**
	 * 依据转换字典更换Map的key
	 * */
	public static Map<String,Object> convertKeyByDictMap(Map<String,Object> data,Map<String,String> dictMap) {
		Map<String,Object> rs=new HashMap<>();
		for(Map.Entry<String, Object> e:data.entrySet()) {
			String key=e.getKey();
			Object value=e.getValue();
			String newKey=dictMap.get(key);
			if(newKey!=null)
				rs.put(newKey, value);
		}
		return rs;
	}
	
	public static final Map<String,Object> flat(Map<String,Object> data){
		Map<String,Object> rs=new HashMap<>();
		for(Map.Entry<String, Object> e:data.entrySet()) {
			String key=e.getKey();
			Object value=e.getValue();
			if(Map.class.isAssignableFrom(value.getClass())) {
				Map<String,Object> v=(Map<String,Object>)value;
				Map<String,Object> flatMap=flat(v);
				for(Map.Entry<String, Object> e2:flatMap.entrySet()) {
					StringBuilder sb=new StringBuilder(key)
							.append(".")
							.append(e2.getKey());
					rs.put(sb.toString(), e2.getValue());
				}
			}else {
				rs.put(key, value);
			}
		}
		return rs;
	}
	
	public static final Map<String,Object> expand(Map<String,Object> data){
		Map<String,Object> rs=new HashMap<>();
		for(Map.Entry<String, Object> e:data.entrySet()) {
			String key=e.getKey();
			Object value=e.getValue();
			if(StringUtils.contains(key, ".")) {
				String[] keys=StringUtils.split(key,".");
				Map<String,Object> top=rs;
				for(int i=0;i<keys.length;i++) {
					String k=keys[i];
					if(i<keys.length-1) {
						Object v=top.get(k);
						if(v==null) {
							v=new HashMap<>();
							top.put(k, v);
						}
						top=(Map<String, Object>) v;
					}else {
						top.put(k, value);
					}
					
				}
			}else {
				rs.put(key, value);
			}
		}
		return rs;
	}
	
	public static <T> Map<T,Map<String,Object>> groupBy(List<Map<String,Object>> list,String key,Class<T> type){
		if(CollectionUtils.isEmpty(list))return Collections.emptyMap();
		Map<T,Map<String,Object>> rs=new HashMap<>(list.size());
		for(Map<String,Object> n:list) {
			@SuppressWarnings("unchecked")
			T k=(T) n.get(key);
			rs.put(k, n);
		}
		return rs;
	}
	
	@SuppressWarnings("unchecked")
	public static void putExtendedValue(Map<String,Object> holder,String key,Object value) {
		if(StringUtils.contains(key, ".")) {
			String[] keys=StringUtils.split(key,".");
			Map<String,Object> top=holder;
			for(int i=0;i<keys.length;i++) {
				String k=keys[i];
				if(i<keys.length-1) {
					Object v=top.get(k);
					if(v==null) {
						v=new HashMap<>();
						top.put(k, v);
					}
					top=(Map<String, Object>) v;
				}else {
					top.put(k, value);
				}
				
			}
		}else {
			holder.put(key, value);
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void putMultiValue(Map<String,Object> container,String key,Object value) {
		Object v=container.get(key);
		if(v==null) {
			container.put(key, value);
		}else {
			if(Collections.class.isAssignableFrom(v.getClass())) {
				Collection<Object> array=(Collection<Object>)v;
				array.add(value);
			}else {
				Collection<Object> array=new LinkedHashSet<>();
				array.add(v);
				array.add(value);
				container.put(key, array);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static <T,V> Map<T,V> groupListBeanBy(List<V> list,String key,Class<T> type){
		if(CollectionUtils.isEmpty(list))return Collections.emptyMap();
		Map<T,V> rs=new HashMap<>(list.size());
		try {
		Field f=list.get(0).getClass().getDeclaredField(key);
		f.setAccessible(Boolean.TRUE);
		for(V n:list) {
			T v=(T) f.get(n);
			rs.put(v, n);
		}
		return rs;
		}catch(Exception e) {
			return rs;
		}
	}
	
	public static void applyIf(Map<String,Object> dest,Map<String,Object> src) {
		for(Map.Entry<String, Object> e:src.entrySet()) {
			Object v=e.getValue();
			if(v!=null) {
				if(Number.class.isAssignableFrom(v.getClass())||String.class.isAssignableFrom(v.getClass())) {
					dest.put(e.getKey(), v);
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T applyIfDeep(T dest,Object src) {
		Map<String,Object> sdesc=objectMapper.convertValue(dest, Map.class);
		Map<String,Object> ssrc=objectMapper.convertValue(src, Map.class);
		applyIfDeep(sdesc,ssrc);
		return (T) objectMapper.convertValue(sdesc, dest.getClass());
	}
	
	@SuppressWarnings("unchecked")
	public static void applyIfDeep(Map<String,Object> dest,Map<String,Object> src) {
		for(Map.Entry<String, Object> e:src.entrySet()) {
			Object v=e.getValue();
			if(v!=null) {
				if(Number.class.isAssignableFrom(v.getClass())||String.class.isAssignableFrom(v.getClass())
						||Boolean.class.isAssignableFrom(v.getClass())) {
					dest.put(e.getKey(), v);
				}else if(v.getClass().isArray()||Array.class.isAssignableFrom(v.getClass())||Collection.class.isAssignableFrom(v.getClass())){
					dest.put(e.getKey(), v);
				}else {
					System.out.println(v.getClass());
					Map<String,Object> x=objectMapper.convertValue(v, Map.class);
					Object d=dest.get(e.getKey());
					if(d!=null) {
						Map<String,Object> y=objectMapper.convertValue(d, Map.class);
						applyIfDeep(y,x);
						dest.put(e.getKey(), y);
					}else {
						dest.put(e.getKey(), x);
					}
				}
			}
		}
	}
	
	public static Set<Long> collectIds(List<Map<String,Object>> list,String key){
		Set<Long> ids=new HashSet<>();
		for(Map<String,Object> n:list) {
			Long id=((Number)n.get(key)).longValue();
			ids.add(id);
		}
		return ids;
	}

}
